#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

#include <unistd.h>
//#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// struct sembuf {
//     unsigned short  semval;   /* semaphore value */
//     unsigned short  semzcnt;  /* # waiting for zero */
//     unsigned short  semncnt;  /* # waiting for increase */
//     pid_t           sempid;   /* ID of process that did last op */
// };

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};

int semid;

void *routine(void *args) {

    // 先进行P操作
    struct sembuf buf;
    memset(&buf,0,sizeof(buf));
    buf.sem_op=-1;
    buf.sem_num=1; // 操作下标为1的信号量

    semop(semid,&buf,1);

    int n=10;
    while(n--) {
        printf("routine %d\n",n);
        sleep(1);
    }

    return NULL;
}

// 信号灯
// 先让主线程打印,再让routine线程打印
int main() {

    key_t key=ftok("semTest.c",666);
    if(-1==key) {
        perror("ftok");
        exit(-1);
    }

    // 获取信号灯
    semid=semget(key,3,IPC_CREAT|IPC_EXCL|0666);
    system("ipcs -s");

    // 初始化信号灯中的第二个信号量(下标为1)
    union semun mysemun;
    mysemun.val=0;
    // int semctl(int semid, int semnum, int cmd, ...);
    // semnum: 下标
    semctl(semid,1,SETVAL,mysemun);

    // 创建线程
    pthread_t tid;
    pthread_create(&tid,NULL,routine,NULL);

    int n=10;
    while(n--) {
        printf("main %d\n",n);
        sleep(1);
    }

    // 打印完成进行v操作
    struct sembuf buf;
    memset(&buf,0,sizeof(buf));
    buf.sem_op=1; // 1=V -1=P
    buf.sem_num=1;
    semop(semid,&buf,1);

    pthread_join(tid,NULL);

    semctl(semid,0,IPC_RMID);
    system("ipcs -s");

    return 0;
}

/*
////////////////////////////////////////////////////////////////////////////////////////////////
//信号量
// 操作数
struct Entry {

    int x;
    int y;

};

// 循环队列---交易场所
#define CQUEUESIZE 10
struct Entry cqueue[CQUEUESIZE];
int head=0;
int tail=0;

sem_t space_sem; // 标识空间量
sem_t data_sem; // 标识数据量

void *producer(void *args) {

    struct Entry entry;

    while(true) {
        entry.x=rand()%100;
        sleep(1);
        entry.y=rand()%100;

        sem_wait(&space_sem); // 申请空间

        cqueue[tail++]=entry;
        if(tail>=CQUEUESIZE) {
            tail=0;
        }

        sem_post(&data_sem); // 生产数据

        printf("pthread: %d | production\n",pthread_self());
    }

    return NULL;
}

void *consumer(void *args) {

    struct Entry entry;

    while(true) {

        sem_wait(&data_sem); // 申请数据

        entry=cqueue[head++];
        if(head>=CQUEUESIZE) {
            head=0;
        }

        sem_post(&space_sem); // 生产空间

        printf("pthread: %d | %d + %d = %d\n",pthread_self(),entry.x,entry.y,entry.x+entry.y);
        usleep(5000);
    }

}

int main() {

    srand((unsigned int)time(NULL));

    // int sem_init(sem_t *sem, int pshared, unsigned int value);
    // pshared: 0表示线程间,非0表示进程间
    sem_init(&space_sem,0,CQUEUESIZE);
    sem_init(&data_sem,0,0);

    pthread_t producarr[3];

    for(int i=0;i<3;++i) {
        pthread_create(producarr+i,NULL,producer,NULL);
    }

    pthread_t consumarr[3];

    for(int i=0;i<3;++i) {
        pthread_create(consumarr+i,NULL,consumer,NULL);
    }

    while(true) {

    }

    return 0;
}

*/